﻿using Microsoft.UI.Xaml.Hosting;

namespace DimensionsHelper.Client.WinUi.Controls;

public partial class BorderGridRangeCollection : IList<BorderGridRange>
{
    private static readonly float EnlargeScale = 4;
    private static readonly Vector3 GlobalOffset = new(-EnlargeScale / 2, -EnlargeScale / 2, 0);
    private readonly Compositor _compositor;
    private readonly BorderGrid _grid;

    private readonly List<BorderGridRange> _innerList = [];
    private BorderGridCell[,] _cells;

    public BorderGridRangeCollection(BorderGrid grid)
    {
        _grid = grid;
        _compositor = ElementCompositionPreview.GetElementVisual(grid).Compositor;
        Visual = _compositor.CreateShapeVisual();
        _cells = new BorderGridCell[grid.Rows, grid.Columns];
        ElementCompositionPreview.SetElementChildVisual(_grid, Visual);
    }

    public static Vector2 CellOffset => new(EnlargeScale / 2, EnlargeScale / 2);


    public ShapeVisual Visual { get; }


    public bool IsInitialized { get; private set; }

    public BorderGridRange this[int index]
    {
        get => _innerList[index];
        set
        {
            _innerList[index] = value;
            value.OwnerGrid = _grid;
        }
    }


    public int Count => _innerList.Count;

    public bool IsReadOnly => false;


    public void Add(BorderGridRange item)
    {
        _innerList.Add(item);
        SetOwner(item);
    }


    public void Clear() => _innerList.Clear();


    public bool Contains(BorderGridRange item) => _innerList.Contains(item);


    public void CopyTo(BorderGridRange[] array, int arrayIndex) => _innerList.CopyTo(array, arrayIndex);


    public IEnumerator<BorderGridRange> GetEnumerator() => _innerList.GetEnumerator();


    public int IndexOf(BorderGridRange item) => _innerList.IndexOf(item);


    public void Insert(int index, BorderGridRange item) => _innerList.Insert(index, item);


    public bool Remove(BorderGridRange item) => _innerList.Remove(item);


    public void RemoveAt(int index) => _innerList.RemoveAt(index);


    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();


    public void UpdateVisualSize()
    {
        Visual.Size = new Vector2((float)_grid.ActualWidth + EnlargeScale, (float)_grid.ActualHeight + EnlargeScale);
        // 在画边框时，如果绘图尺寸和容器大小相同的画，容器外边框线将被截断一半，
        // 所以，将绘图对象尺寸放大一些，然后将绘图对象向左上方向偏移一半。
        Visual.Offset = GlobalOffset;
    }


    private static void BuildRangeMap(BorderGridRange?[,] targetMap, BorderGridRange range)
    {
        for (int r = range.Row; r < range.Row + range.RowSpan; r++)
        {
            for (int c = range.Column; c < range.Column + range.ColumnSpan; c++)
            {
                targetMap[r, c] = range;
            }
        }

        foreach (BorderGridRange sub in range)
        {
            BuildRangeMap(targetMap, sub);
        }
    }


    public void Initialize()
    {
        if (IsInitialized)
        {
            return;
        }

        UpdateVisualSize();

        BorderGridRange?[,] rangeMap = new BorderGridRange[_grid.Rows, _grid.Columns];
        foreach (BorderGridRange range in _innerList)
        {
            BuildRangeMap(rangeMap, range);
        }

        _cells = new BorderGridCell[_grid.Rows, _grid.Columns];
        double rowOffset = 0;
        for (int r = 0; r < _grid.Rows; r++)
        {
            double colOffset = 0;
            for (int c = 0; c < _grid.Columns; c++)
            {
                BorderGridCell cell = InitCell(r, c);
                cell.ContainingRange = rangeMap[r, c];
                cell.UpdateVertexCoordinates(rowOffset, colOffset);
                cell.UpdateBorder(false);
                colOffset += _grid.ColumnDefinitions[c].ActualWidth;
            }

            rowOffset += _grid.RowDefinitions[r].ActualHeight;
        }

        IsInitialized = true;
    }


    private BorderGridCell InitCell(int row, int col)
    {
        BorderGridCell cell = new(_grid, _compositor, Visual, row, col);

        if (row > 0)
        {
            BorderGridCell top = _cells[row - 1, col];
            cell.Top = top;
            top.Bottom = cell;
        }

        if (col > 0)
        {
            BorderGridCell left = _cells[row, col - 1];
            cell.Left = left;
            left.Right = cell;
        }

        if (row == _grid.Rows - 1)
        {
            cell.AddFlags(BorderCellFlags.BottomBound);
        }

        if (col == _grid.Columns - 1)
        {
            cell.AddFlags(BorderCellFlags.RightBound);
        }

        _cells[row, col] = cell;
        return cell;
    }


    public BorderGridCell? GetCell(int row, int col)
    {
        if (row < 0 || row >= _cells.GetLength(0) || col < 0 || col >= _cells.GetLength(1))
        {
            return null;
        }

        return _cells[row, col];
    }


    public void UpdateBorder()
    {
        double rowOffset = 0;

        for (int r = 0; r < _grid.Rows; r++)
        {
            double colOffset = 0;
            for (int c = 0; c < _grid.Columns; c++)
            {
                BorderGridCell cell = _cells[r, c];
                cell.UpdateVertexCoordinates(rowOffset, colOffset);
                cell.UpdateBorder(false);
                colOffset += _grid.ColumnDefinitions[c].ActualWidth;
            }

            rowOffset += _grid.RowDefinitions[r].ActualHeight;
        }
    }


    private void SetOwner(BorderGridRange range)
    {
        range.OwnerGrid = _grid;
        foreach (BorderGridRange childRange in range)
        {
            SetOwner(childRange);
        }
    }
}